<?php

	/**
	 * @package		CoreProject
	 * @subpackage	Img
	 *
	 * @author		Ildar N. Shaimordanov <ildar-sh@mail.ru>
	 * @link		http://debugger.ru/articles/thumbnails
	 *
	 * cpr_Img_Thumbnail
	 * 
	 */

	class cpr_Img_Thumbnail
	{
		const THUMBNAIL_METHOD_SCALE_MAX	= 0;
		const THUMBNAIL_METHOD_SCALE_MIN	= 1;
		const THUMBNAIL_METHOD_CROP			= 2;

		const THUMBNAIL_ALIGN_CENTER		= 0;
		const THUMBNAIL_ALIGN_LEFT			= -1;
		const THUMBNAIL_ALIGN_RIGHT			= +1;
		const THUMBNAIL_ALIGN_TOP			= -1;
		const THUMBNAIL_ALIGN_BOTTOM		= +1;
		
		private function __construct() {}
		
		/**
		 * Create a GD image resource from given input.
		 *
		 * This method tried to detect what the input, if it is a file the
		 * createImageFromFile will be called, otherwise createImageFromString()
		 *
		 * @param	mixed	$_input
		 * @return	resource|false
		 */
		public static function imageCreate($_input)
		{
			if (is_file($_input))
			{
				return self::imageCreateFromFile($_input);
			}
			else if (is_string($_input))
			{
				return self::imageCreateFromString($_input);
			}
			else
			{
            	return $_input;
			}
		}
		
		/**
		 * Create a GD image resource from file (JPEG, PNG support).
		 *
		 * @param	string	$_filename
		 * @return	mixed|false
		 */
		public static function imageCreateFromFile($_filename)
		{
			if (!is_file($_filename) || !is_readable($_filename))
			{
				return false;
			}

			/* determine image format */
			list( , , $_type) = getimagesize($_filename);
			
			switch ($_type)
			{
				case IMAGETYPE_JPEG:
					return imagecreatefromjpeg($_filename);
					break;
				case IMAGETYPE_PNG:
					return imagecreatefrompng($_filename);
					break;
			}
			
	        return false;
		}
		
		/**
		 * Create a GD image resource from a string data.
		 *
		 * @param	string	$_string
		 * @return	mixed|false
		 */
		public static function imageCreateFromString($_string)
		{
			if (!is_string($_string) || empty($_string))
			{
				return false;
			}
			
			return imagecreatefromstring($_string);
		}
		
		/**
		 *
		 * Display rendered image (send it to browser or to file).
		 * This method is a common implementation to render and output an image.
		 * The method calls the render() method automatically and outputs the
		 * image to the browser or to the file.
		 *
		 * @param	mixed	$_input
		 * @param	mixed	$_output = null
		 * @param	array	$_options = array()
		 * @return	bool
		 */
		public static function output($_input, $_output = null, $_options = array())
		{
			/* load source file and render image */
			$_renderImage = self::render($_input, $_options);
			if (!$_renderImage)
			{
				return false;
			}
			
			/* set output image type by default PNG image */
			$_type = isset($_options['type']) ? $_options['type'] : IMAGETYPE_PNG;
			
			/* before output to browsers send appropriate headers */
			if (empty($_output))
			{
				$_content_type = image_type_to_mime_type($_type);
				if (!headers_sent())
				{
					header('Content-Type: '.$_content_type);
				}
				else
				{
					return false;
				}
			}
			
			$_result = false;
			/* define outputing function */
			switch ($_type)
			{
				case IMAGETYPE_PNG:
					$_result = empty($_output) ? imagepng($_renderImage) : imagepng($_renderImage, $_output);
					break;
				case IMAGETYPE_JPEG:
					$_result = empty($_output) ? imagejpeg($_renderImage) : imagejpeg($_renderImage, $_output);
					break;
				default:
					return $_result;
			}
			
			/* output image (to browser or to file) */
			if (!$_result)
			{
				return $_result;
			}
			
			imagedestroy($_renderImage);
			
			return true;
		}
		
		/**
		 * Draw thumbnail result to resource.
		 *
		 * @param	mixed	$_input
		 * @param	array	$_options = array()
		 * @return	bool
		 */
		public static function render($_input, $_options = array())
		{
			$_width  = $_height = $_X = $_Y = $_W = $_H = 0;

			/* create the source image */
			$_sourceImage = self::imageCreate($_input);
			if (!is_resource($_sourceImage))
			{
				return false;
			}
			$_sourceWidth  = imagesx($_sourceImage);
			$_sourceHeight = imagesy($_sourceImage);
			
			/* set default options */
			static $_defOptions = array(
						'width'   => 150,
						'height'  => 150,
						'method'  => THUMBNAIL_METHOD_SCALE_MAX,
						'percent' => 0,
						'halign'  => THUMBNAIL_ALIGN_CENTER,
						'valign'  => THUMBNAIL_ALIGN_CENTER
					);
			foreach ($_defOptions as $_k => $_v)
			{
				if (!isset($_options[$_k]))
				{
					$_options[$_k] = $_v;
				}
			}
			
			$_defalign = ($_sourceHeight > $_sourceWidth) ? THUMBNAIL_ALIGN_TOP : THUMBNAIL_ALIGN_CENTER;
			$_options['valign'] = $_defalign;
			
			/* estimate a rectangular portion of the source image and a size of the target image */
			if ($_options['method'] == THUMBNAIL_METHOD_CROP)
			{
				$_W = ($_options['percent']) ? floor($_options['percent'] * $_sourceWidth) : $_options['width'];
				$_H = ($_options['percent']) ? floor($_options['percent'] * $_sourceHeight) : $_options['height'];
			
				$_width  = $_W;
				$_height = $_H;
			
				$_Y = self::_coord($_options['valign'], $_sourceHeight, $_H);
				$_X = self::_coord($_options['halign'], $_sourceWidth,  $_W);
			}
			else
			{
				$_X = 0;
				$_Y = 0;
			
				$_W = $_sourceWidth;
				$_H = $_sourceHeight;
			
				if ($_options['percent'])
				{
					$_width = floor($_options['percent'] * $_W);
					$_height = floor($_options['percent'] * $_H);
				}
				else
				{
					$_width  = $_options['width'];
					$_height = $_options['height'];
			
					if ($_options['method'] == THUMBNAIL_METHOD_SCALE_MIN)
					{
						$_Ww = $_W / $_width;
						$_Hh = $_H / $_height;
						
						if ( $_Ww > $_Hh )
						{
							$_W = floor($_width * $_Hh);
							$_X = self::_coord($_options['halign'], $_sourceWidth, $_W);
						}
						else
						{
							$_H = floor($_height * $_Ww);
							$_Y = self::_coord($_options['valign'], $_sourceHeight, $_H);
						}
					}
					else
					{
						if ($_H > $_W)
						{
							$_width  = floor($_height / $_H * $_W);
						}
						else
						{
							$_height = floor($_width / $_W * $_H);
						}
					}
				}
			}
			
			/* create the target image */
			$_targetImage = (function_exists('imagecreatetruecolor'))
					? imagecreatetruecolor($_width, $_height) : imagecreate($_width, $_height);
		        
			if (!is_resource($_targetImage))
			{
				return false;
			}

			$_result = null;
			/* copy the source image to the target image */
			if ($_options['method'] == THUMBNAIL_METHOD_CROP)
			{
				$_result = imagecopy($_targetImage, $_sourceImage, 0, 0, $_X, $_Y, $_W, $_H);
			}
			else if (function_exists('imagecopyresampled'))
			{
				$_result = imagecopyresampled($_targetImage, $_sourceImage, 0, 0, $_X, $_Y, $_width, $_height, $_W, $_H);
			}
			else
			{
				$_result = imagecopyresized($_targetImage, $_sourceImage, 0, 0, $_X, $_Y, $_width, $_height, $_W, $_H);
			}
			
			if (!is_resource($_result))
			{
				return false;
			}
		
			imagedestroy($_sourceImage);
			
			return $_targetImage;
		}
				
		/**
		 * @param	int	$_align
		 * @param	int	$_param
		 * @param	int	$_src
		 * @return	int
		 */
		private static function _coord($_align, $_param, $_src)
	    {
			$_result = 0;
			if ($_align < self::THUMBNAIL_ALIGN_CENTER)
			{
            	$_result = 0;
			}
			else if ($_align > self::THUMBNAIL_ALIGN_CENTER)
			{
				$_result = $_param - $_src;
			}
			else
			{
				$_result = ($_param - $_src) >> 1;
			}
			
			return $_result;
		}
	}
